package com.yang.xiaohoshuauth.service.impl;

import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.yang.common.constant.RedisKeyConstants;
import com.yang.common.constant.RoleConstants;
import com.yang.common.exception.BizException;
import com.yang.common.response.Response;
import com.yang.common.utils.JsonUtil;
import com.yang.xiaohoshuauth.domain.dataobject.PermissionDO;
import com.yang.xiaohoshuauth.domain.dataobject.UserDO;
import com.yang.xiaohoshuauth.domain.dataobject.UserRoleDO;
import com.yang.xiaohoshuauth.domain.mapper.RoleDOMapper;
import com.yang.xiaohoshuauth.domain.mapper.UserDOMapper;
import com.yang.xiaohoshuauth.domain.mapper.UserRoleDOMapper;
import com.yang.xiaohoshuauth.domain.vo.UserLoginReqVO;
import com.yang.xiaohoshuauth.enums.DeletedEnum;
import com.yang.xiaohoshuauth.enums.LoginTypeEnum;
import com.yang.xiaohoshuauth.enums.ResponseCodeEnum;
import com.yang.xiaohoshuauth.enums.StatusEnum;
import com.yang.xiaohoshuauth.service.UserService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.cache.TransactionalCacheManager;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;


@Service
@Slf4j
public class UserServiceImpl implements UserService {


    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private UserDOMapper userDOMapper;

    @Resource
    private UserRoleDOMapper userRoleDOMapper;

    @Resource
    private TransactionTemplate transactionTemplate;

    /**
     * 登录和注册
     * @param userLoginReqVO
     * @return
     */
    @Override
    public Response<String> loginAndRegister(UserLoginReqVO userLoginReqVO) {
        Integer type = userLoginReqVO.getType();
        String phone = userLoginReqVO.getPhone();

        Long userId = null;

        LoginTypeEnum loginTypeEnum = LoginTypeEnum.valueOf(type);

        // 1、判断登录类型
        switch (loginTypeEnum) {
            // 验证码
            case VERIFICATION_CODE:
                String code = userLoginReqVO.getCode();
                // 校验验证码是否为空
                Preconditions.checkArgument(StringUtils.isBlank(code),"验证码不能为空");
                // 获取内存中的验证码
                String sentCode = (String) redisTemplate.opsForValue().get(RedisKeyConstants.buildVerificationCodeKey(phone));

                // 校验验证码是否喝redis中存储的一样
                if(!code.equals(sentCode)){
                    return Response.fail(ResponseCodeEnum.VERIFICATION_CODE_ERROR);
                }
                // 判断是不是首次
                UserDO userDO = userDOMapper.selectByPhone(phone);


                log.info("==> 用户是否注册, phone: {}, userDO: {}", phone, JsonUtil.toJsonString(userDO));

                // 是的话直接注册
                if(Objects.isNull(userDO)){
                    // todo 注册
                    userId = registerUser(phone);
                }// 已经注册，获取userId
                else {
                    userId = userDO.getId();
                }
                break;
            // 2.1账号密码登录
            case PASSWORD :
                // todo
                String password = userLoginReqVO.getPassword();
                // 判断参数
                if(StringUtils.isBlank(password) || StringUtils.isBlank(phone)){
                    return Response.fail(ResponseCodeEnum.PARAM_NOT_VALID);
                }
                // 校验账号密码是否正确

                // 正确直接通过
                // 不正确抛出异常

                // 2.2、手机验证码登录
                // 校验验证码是否正确
                // 判断是不是首次注册，是的话直接注册一个账号
            default:
                break;
        }

        // sa-token登录
        StpUtil.login(userId);

        // 获取token令牌
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();

        return Response.success(tokenInfo.getTokenValue());
    }

    /**
     * 系统自动注册用户
     * @param phone
     * @return
     */
    public Long registerUser(String phone) {
        return transactionTemplate.execute(status -> {
            try {
                // 获取全局自增的小哈书 ID
                Long xiaohashuId = redisTemplate.opsForValue().increment(RedisKeyConstants.XIAOHOSHU_ID_GENERATOR_KEY);

                UserDO userDO = UserDO.builder()
                        .phone(phone)
                        .xiaohashuId(String.valueOf(xiaohashuId)) // 自动生成小红书号 ID
                        .nickname("小红薯" + xiaohashuId) // 自动生成昵称, 如：小红薯10000
                        .status(StatusEnum.ENABLE.getValue()) // 状态为启用
                        .createTime(LocalDateTime.now())
                        .updateTime(LocalDateTime.now())
                        .isDeleted(DeletedEnum.NO.getValue()) // 逻辑删除
                        .build();

                // 添加入库
                userDOMapper.insert(userDO);

                // 获取刚刚添加入库的用户 ID
                Long userId = userDO.getId();

                // 给该用户分配一个默认角色
                UserRoleDO userRoleDO = UserRoleDO.builder()
                        .userId(userId)
                        .roleId(RoleConstants.COMMON_USER_ROLE_ID)
                        .createTime(LocalDateTime.now())
                        .updateTime(LocalDateTime.now())
                        .isDeleted(DeletedEnum.NO.getValue())
                        .build();
                userRoleDOMapper.insert(userRoleDO);

                // 将该用户的角色 ID 存入 Redis 中
                List<Long> roles = Lists.newArrayList();
                roles.add(RoleConstants.COMMON_USER_ROLE_ID);
                String userRolesKey = RedisKeyConstants.buildVerificationCodeKey(phone);
                redisTemplate.opsForValue().set(userRolesKey, JsonUtil.toJsonString(roles));

                return userId;
            } catch (Exception e) {
                // 标记事物会滚
                status.setRollbackOnly();
                log.error("注册用户失败！，异常原因为{}", e.getMessage());
                return null;
            }
        });

    }
}
